package com.zlc.workflow.core.task.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.pagehelper.PageHelper;
import com.zlc.common.common.constant.WorkflowConstant;
import com.zlc.common.common.entity.PageVo;
import com.zlc.common.core.process.mapper.StoneProcessInstanceMapper;
import com.zlc.common.core.process.mapper.StoneTaskMapper;
import com.zlc.common.core.process.model.entity.StoneTaskHistory;
import com.zlc.common.core.process.model.vo.StoneProcessInstanceVo;
import com.zlc.common.core.process.model.vo.StoneTaskHistoryVo;
import com.zlc.common.core.process.model.vo.StoneTaskVo;
import com.zlc.common.core.process.service.StoneProcessLogService;
import com.zlc.common.core.process.service.StoneProcessService;
import com.zlc.common.core.process.service.StoneTaskHistoryService;
import com.zlc.common.core.process.service.StoneTaskService;
import com.zlc.common.core.user.model.vo.MayanPlatUserVo;
import com.zlc.common.core.user.service.MayanPlatUserService;
import com.zlc.common.model.process.instance.FlowInstanceVo;
import com.zlc.common.model.process.instance.QueryInstanceParam;
import com.zlc.common.model.user.UserInfo;
import com.zlc.common.util.AssertUtils;
import com.zlc.common.util.BeanCopyUtils;
import com.zlc.common.util.PlatUtils;
import com.zlc.common.util.UserHelper;
import com.zlc.workflow.constant.ProcessLogTypeEnum;
import com.zlc.workflow.constant.TaskOperateTypeEnum;
import com.zlc.workflow.constant.TaskStatusEnum;
import com.zlc.workflow.constant.WorkflowStatusEnum;
import com.zlc.workflow.core.process.cmd.ParseXmlToJsonCmd;
import com.zlc.workflow.core.process.model.FlowProcess;
import com.zlc.workflow.core.process.model.design.AntDesignEntity;
import com.zlc.workflow.core.process.model.design.AntDesignUtil;
import com.zlc.workflow.core.process.model.design.AntLink;
import com.zlc.workflow.core.process.model.design.AntNode;
import com.zlc.workflow.core.task.model.AddTaskActorParam;
import com.zlc.workflow.core.task.model.ApproveHistTaskVo;
import com.zlc.workflow.core.task.model.CurrentTaskVo;
import com.zlc.common.model.process.task.FlowTaskVo;
import com.zlc.common.model.process.task.QueryTaskParam;
import com.zlc.workflow.core.task.model.TransferTaskParam;
import com.zlc.workflow.core.task.model.WorkflowTraceVo;
import com.zlc.workflow.core.task.service.TaskService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class TaskServiceImpl implements TaskService {

    @Autowired
    private StoneTaskHistoryService stoneTaskHistoryService;
    @Autowired
    private StoneTaskService stoneTaskService;
    @Autowired
    private StoneProcessService stoneProcessService;
    @Autowired
    private StoneTaskMapper stoneTaskMapper;
    @Autowired
    private StoneProcessInstanceMapper stoneProcessInstanceMapper;
    @Autowired
    private MayanPlatUserService mayanPlatUserService;
    @Autowired
    private StoneProcessLogService stoneProcessLogService;

    @Override
    public WorkflowTraceVo getTraceList(String processId,String processInstanceId) {
        WorkflowTraceVo traceVo = new WorkflowTraceVo();
        traceVo.setProcessInstanceId(processInstanceId);
        String content = stoneProcessService.content(processId);
        traceVo.setContent(content);
        //流程内容
        FlowProcess process = new ParseXmlToJsonCmd(content).execute();
        traceVo.setProcess(process);
        //ant
        AntDesignEntity entity = AntDesignUtil.buildAntDesignEntity(process, processId);
        traceVo.setDesign(entity);
        //进行中的任务
        List<StoneTaskVo> taskVos = stoneTaskService.getByProcessInstanceId(processInstanceId);
        List<CurrentTaskVo> currentTaskVos = taskVos.stream().flatMap(s -> {
            CurrentTaskVo taskVo = new CurrentTaskVo();
            taskVo.setTaskId(s.getTaskId());
            taskVo.setNodeId(s.getNodeId());
            return Stream.of(taskVo);
        }).collect(Collectors.toList());
        traceVo.setCurrentTasks(currentTaskVos);
        //历史任务
        List<StoneTaskHistoryVo> historyVos = stoneTaskHistoryService.getByProcessInstanceId(processInstanceId);
        List<String> paths = new ArrayList<>();
        for(StoneTaskHistoryVo historyVo : historyVos){
            String path = historyVo.getPath();
            if(!ObjectUtils.isEmpty(path)){
                String[] pathArr = path.split(";");
                paths.addAll(Arrays.asList(pathArr));
            }
        }
        List<String> dictPaths = paths.stream().distinct().collect(Collectors.toList());
        traceVo.setPaths(dictPaths);
        loadDesign(entity,currentTaskVos,dictPaths);
        return traceVo;
    }

    private void loadDesign(AntDesignEntity entity,List<CurrentTaskVo> currentTaskVos,List<String> paths){
        List<AntNode> nodeList = entity.getNodeList();
        List<AntLink> linkList = entity.getLinkList();
        List<String> activeNodeIds = currentTaskVos.stream().map(CurrentTaskVo::getNodeId).collect(Collectors.toList());
        nodeList.forEach(node->{
            if(activeNodeIds.contains(node.getId())){
                node.setColor("#f50");
                node.setActive(true);
            }
        });
        linkList.forEach(link->{
            if(paths.contains(link.getId())){
                link.setColor("#65cf38");
                Map<String, Object> cls = link.getCls();
                if(!ObjectUtils.isEmpty(cls)){
                    cls.put("linkColor","#f50");
                }
            }
        });
    }

    @Override
    public PageVo<FlowTaskVo> queryTaskTodo(QueryTaskParam param) {
        PageHelper.startPage(param.getPageNum(),param.getPageSize());
        List<FlowTaskVo> taskVos = stoneTaskMapper.getTaskList(param);
        taskVos.forEach(task->{
            task.setStatusName(TaskStatusEnum.getStatusName(task.getStatus()));
        });
        return PageVo.page(taskVos);
    }

    @Override
    public PageVo<FlowInstanceVo> queryInstancePage(QueryInstanceParam param) {
        PageHelper.startPage(param.getPageNum(),param.getPageSize());
        List<FlowInstanceVo> instanceVos = stoneProcessInstanceMapper.getHistInstanceList(param);
        List<String> draftInstanceIds = instanceVos.stream().filter(i -> WorkflowStatusEnum.DRAFT.getStatus().equals(i.getStatus()))
                .map(FlowInstanceVo::getProcessInstanceId).collect(Collectors.toList());
        List<StoneTaskVo> draftTasks = new ArrayList<>();
        if(!ObjectUtils.isEmpty(draftInstanceIds)) {
            draftTasks = stoneTaskService.getByProcessInstanceIds(draftInstanceIds);
        }
        List<StoneTaskVo> finalDraftTasks = draftTasks;
        instanceVos.forEach(instance->{
            instance.setStatusName(WorkflowStatusEnum.getStatusName(instance.getStatus()));

            StoneTaskVo stoneTaskVo = finalDraftTasks.stream().filter(d -> d.getProcessInstanceId().equals(instance.getProcessInstanceId()))
                    .findFirst().orElse(null);
            if(!ObjectUtils.isEmpty(stoneTaskVo)){
                instance.setTaskId(stoneTaskVo.getTaskId());
                instance.setNodeId(stoneTaskVo.getNodeId());
                instance.setNodeName(stoneTaskVo.getNodeName());
            }
        });
        return PageVo.page(instanceVos);
    }

    @Override
    public PageVo<FlowTaskVo> queryTaskHistory(QueryTaskParam param) {
        PageHelper.startPage(param.getPageNum(),param.getPageSize());
        List<FlowTaskVo> taskVos = stoneTaskMapper.getHistTaskList(param);
        taskVos.forEach(task->{
            task.setStatusName(TaskStatusEnum.getStatusName(task.getStatus()));
            task.setOperateTypeName(TaskOperateTypeEnum.getOperateTypeName(task.getOperateType()));
        });
        return PageVo.page(taskVos);
    }

    @Override
    public List<ApproveHistTaskVo> getTaskApprovalHist(String processInstanceId) {
        LambdaQueryWrapper<StoneTaskHistory> wrapper = stoneTaskHistoryService.wrapper()
                .eq(StoneTaskHistory::getProcessInstanceId, processInstanceId)
                .orderByAsc(StoneTaskHistory::getExecuteTime);
        List<StoneTaskHistoryVo> historyVos = stoneTaskHistoryService.queryVoList(wrapper);
        if(ObjectUtils.isEmpty(historyVos)){
            return new ArrayList<>();
        }
        List<StoneTaskHistoryVo> activeTaskList = historyVos.stream().filter(h -> ObjectUtils.isEmpty(h.getExecuteTime()))
                .collect(Collectors.toList());
        List<StoneTaskHistoryVo> taskList = historyVos.stream().filter(h -> !ObjectUtils.isEmpty(h.getExecuteTime()))
                .sorted(Comparator.comparing(StoneTaskHistoryVo::getExecuteTime)).collect(Collectors.toList());
        taskList.addAll(activeTaskList);
        List<ApproveHistTaskVo> approveHistTaskVos = BeanCopyUtils.copyObjList(taskList, ApproveHistTaskVo.class);
        approveHistTaskVos.forEach(task->{
            task.setOperateTypeName(TaskOperateTypeEnum.getOperateTypeName(task.getOperateType()));
        });
        return approveHistTaskVos;
    }

    @Override
    public void addTaskActor(AddTaskActorParam param) {
        AssertUtils.isTrue(!ObjectUtils.isEmpty(param.getTaskId()),"任务ID不可为空");
        StoneTaskVo taskVo = stoneTaskService.getByTaskId(param.getTaskId());
        AssertUtils.isTrue(!ObjectUtils.isEmpty(taskVo),"任务不存在");
        String actorId = param.getActorId();
        AssertUtils.isTrue(!ObjectUtils.isEmpty(actorId),"加签人不可为空");
        MayanPlatUserVo userVo = mayanPlatUserService.queryByUserAccount(actorId);
        AssertUtils.isTrue(!ObjectUtils.isEmpty(userVo),"加签人不存在");

        StoneTaskVo newTaskVo = BeanCopyUtils.copyAndInit(taskVo,StoneTaskVo.class);
        newTaskVo.setActorId(actorId);
        newTaskVo.setActorName(userVo.getUsername());
        newTaskVo.setStatus(WorkflowConstant.TASK_STATUS_PROCESSING);
        newTaskVo.setReceiveTime(new Date());
        newTaskVo.setTaskId(PlatUtils.uuid());
        stoneTaskService.saveVo(newTaskVo);
        //保存任务历史记录
        StoneTaskHistoryVo taskHistoryVo = BeanCopyUtils.copyAndInit(newTaskVo,StoneTaskHistoryVo.class);
        stoneTaskHistoryService.saveVo(taskHistoryVo);
        //保存日志
        UserInfo nowUser = UserHelper.getNowUser();
        stoneProcessLogService.saveLog(taskVo.getProcessInstanceId(),taskVo.getProcessId(),taskVo.getNodeId(),taskVo.getNodeName(),newTaskVo.getTaskId(),
                ProcessLogTypeEnum.ADD_ACTOR.getType(),nowUser.getUserAccount(),"加签新用户-"+userVo.getUsername());
    }

    @Override
    public void transferTask(TransferTaskParam param) {
        AssertUtils.isTrue(!ObjectUtils.isEmpty(param.getTaskId()),"任务ID不可为空");
        StoneTaskVo taskVo = stoneTaskService.getByTaskId(param.getTaskId());
        AssertUtils.isTrue(!ObjectUtils.isEmpty(taskVo),"任务不存在");
        String actorId = param.getNewActorId();
        AssertUtils.isTrue(!ObjectUtils.isEmpty(actorId),"转办人不可为空");
        MayanPlatUserVo userVo = mayanPlatUserService.queryByUserAccount(actorId);
        AssertUtils.isTrue(!ObjectUtils.isEmpty(userVo),"转办人不存在");

        String oldAccount = taskVo.getActorId();
        String oldName = taskVo.getActorName();
        taskVo.setActorId(userVo.getUserAccount());
        taskVo.setActorName(userVo.getUsername());
        stoneTaskService.saveVo(taskVo);

        StoneTaskHistoryVo historyVo = stoneTaskHistoryService.getByTaskId(param.getTaskId());
        historyVo.setActorId(userVo.getUserAccount());
        historyVo.setActorName(userVo.getUsername());
        stoneTaskHistoryService.saveVo(historyVo);

        //添加日志
        UserInfo nowUser = UserHelper.getNowUser();
        String content = "转办操作 "+oldName+"("+oldAccount+") --> "+userVo.getUsername()+"("+userVo.getUserAccount()+")";
        stoneProcessLogService.saveLog(taskVo.getProcessInstanceId(),taskVo.getProcessId(),taskVo.getNodeId(),taskVo.getNodeName(),taskVo.getTaskId(),
                ProcessLogTypeEnum.TRANSFER.getType(),nowUser.getUserAccount(),content);
    }
}
